/* This file is part of FireEscape.
 *
 * FireEscape is the intellectual work of Phillip Cheng & Allen Park.
 * All future releases or modifications of this program must include this
 * attribuation as according to the Creative Commons BY-SA license.
 *
 * FireEscape is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * FireEscape is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FireEscape.  If not, see <http://www.gnu.org/licenses/>.
 *
 * All modifications to this program should include this original statement.
 *
 * If you modify, distribute, or use the program , please contact
 * fireescape@kloudvine.com
 * http://code.google.com/p/fireescape/
 *
 * If you are interested in using FireEscape for your structure, please contact us
 * for support.
 *
 * If you do use FireEscape, please consider donating to the project.
 */
package fireescape;

import java.util.ArrayList;
import java.awt.*;
import java.awt.geom.Point2D;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Represents the room
 * @author chengP
 */
public class Room {

    /*private Rectangle2D.Double rect;
    private Rectangle2D.Double drawnRect;*/
    private RoomShape room;
    private RoomShape drawnRoom;
    private ArrayList<Integer> exits;
    private ArrayList<Integer> botIDs;
    private ArrayList<Integer> closestRoute;    //The route of exits starting with the outside exit to the room
    private double closestDistance;
    private int roomID;
    private int floorNum;
    private boolean injectable;

    /**
     * Constructs a room
     */
    public Room() {
        //this(0, 0, 0, 0, 0, -1, false);
        this(null, 0, 0, false);
    }

    /**
     * Constructs a room
     * @param points The vertices of the room
     * @param roomID The room ID
     * @param floorNum The floor number of the room
     * @param injectable Whether the room can start with bots or not
     */
    //public Room(double x, double y, double xLen, double yLen, int roomID, int floorNum, boolean injectable) {
    public Room(ArrayList<Point2D.Double> points, int roomID, int floorNum, boolean injectable) {
        exits = new ArrayList<Integer>();
        botIDs = new ArrayList<Integer>();
        closestRoute = new ArrayList<Integer>();
        closestDistance = Double.POSITIVE_INFINITY;
        /*rect = new Rectangle2D.Double(x, y, xLen, yLen);
        drawnRect = new Rectangle2D.Double(FireEscapeGUI.scale * x, FireEscapeGUI.scale * y,
        FireEscapeGUI.scale * xLen, FireEscapeGUI.scale * yLen);*/
        if (points != null) {       //means that this room is outside if points is null
            room = new RoomShape(points);
            drawnRoom = new RoomShape(FireEscapeGUI.scale(points));
        }
        this.roomID = roomID;
        this.floorNum = floorNum;
        this.injectable = injectable;
    }

    public void setClosestDistance(double closestDistance) {
        this.closestDistance = closestDistance;
    }

    public double getClosestDistance() {
        return closestDistance;
    }

    public void addClosestRouteToRoute(Route route) {
        for (int i = closestRoute.size() - 1; i >= 0; i--) {
            int exitID = closestRoute.get(i);
            route.addNavPoint(exitID);
        }
    }

    public boolean unreachable() {
        return closestDistance == Double.POSITIVE_INFINITY;
    }

    public double exitToCenter(Exit exit) {
        return FireEscapeGUI.distance(this.getCenterX(), this.getCenterY(), exit.getCenterX(), exit.getCenterY());
    }

    /**
     * Resets the room
     */
    public void reset() {
        this.botIDs = new ArrayList<Integer>();
    }

    /**
     * Draws the room
     * @param g The Graphics2D object used
     */
    public void paint(Graphics2D g) {
        paintRoom(g);
        paintBots(g);
    }

    /**
     * Draws only the room
     * @param g The Graphics2D object used for drawing the room
     */
    public void paintRoom(Graphics2D g) {
        g.setColor(Color.red);
        drawnRoom.draw(g);
        paintExits(g);
    }

    /**
     * Draws only the exits
     * @param g The Graphics2D object used for drawing the exits
     */
    public void paintExits(Graphics2D g) {
        for (int exitID : exits) {
            Exit exit = Map.getExit(exitID);
            exit.paint(g);
        }
    }

    /**
     * Draws only the bots
     * @param g The Graphics2D object used for drawing the bots
     */
    public void paintBots(Graphics2D g) {
        for (int i = 0; i < botIDs.size(); i++) {
            getBot(i).paint(g);
        }
    }

    /**
     * Sets the room ID
     * @param roomID The new room ID
     */
    public void setRoomID(int roomID) {
        this.roomID = roomID;
    }

    /**
     * Returns the room ID
     * @return The room ID
     */
    public int getRoomID() {
        return roomID;
    }

    /**
     * Returns the floor number
     * @return The floor number
     */
    public int getFloorNum() {
        return floorNum;
    }

    /**
     * Returns if the room can start with bots or not
     * @return True if the room can start with bots, false if not
     */
    public boolean injectable() {
        return injectable;
    }

    /**
     * Adds a new bot
     * @param x The new bot
     */
    public void addBot(Bot x) {
        botIDs.add(x.getBotNum());
    }

    /**
     * Returns the bot
     * @param num The bot ID
     * @return The bot
     */
    public Bot getBot(int num) {
        return BotAI.getBot(botIDs.get(num));
    }

    /**
     * Removes a bot
     * @param bot The bot to be removed
     */
    public void removeBot(Bot bot) {
        botIDs.remove(new Integer(bot.getBotNum()));
    }

    /**
     * Returns the number of bots
     * @return The number of bots
     */
    public int getNumBots() {
        return botIDs.size();
    }

    /*public ArrayList<Integer> getBotList() {
    return bots;
    }*/
    /**
     * Returns the rectangle representing the room
     * @return The rectangle representing the room
     */
    public RoomShape getRect() {
        return room;
    }

    /**
     * Adds an exit
     * @param exit The exit to be added
     */
    public void addExit(Exit exit) {
        exits.add(exit.getExitID());
        exit.setRoom(roomID);
    }

    public Exit getExit(int exitNum) {
        return Map.getExit(exits.get(exitNum));
    }

    /*@Deprecated
    public void addExit(double x1, double y1, double x2, double y2) {
    addExit(new Exit(x1, y1, x2, y2));
    }*/
    /**
     * Checks if the exit is on the room's boundaries
     * @param exit The exit to be checked
     * @return True if the exit is on the room's boundaries, false if not
     */
    public boolean exitOnRoom(Exit exit) {
        return exitOnRoom(exit.getX1(), exit.getY1(), exit.getX2(), exit.getY2());
    }

    /**
     * Checks if the exit is on the room's boundaries
     * @param startX The x coordinate of the first corner
     * @param startY The y coordinate of the first corner
     * @param endX The x coordinate of the second corner
     * @param endY The y coordinate of the second corner
     * @return True if the exit is on the room's boundaries, false if not
     */
    public boolean exitOnRoom(double startX, double startY, double endX, double endY) {
        Point2D.Double point1 = room.getPoint(room.numVertices() - 1);
        Point2D.Double point2 = room.getPoint(0);
        if (FireEscapeGUI.segmentOnSegment(new Point2D.Double(startX, startY), new Point2D.Double(endX, endY), point1, point2)) {
            return true;
        }
        for (int i = 1; i < room.numVertices(); i++) {
            point1 = point2;
            point2 = room.getPoint(i);
            if (FireEscapeGUI.segmentOnSegment(new Point2D.Double(startX, startY), new Point2D.Double(endX, endY), point1, point2)) {
                return true;
            }
        }
        return false;
    }

    /*public Exit getExit(int num) {
    return exits.get(num);
    }*/
    /**
     * Returns the number of exits
     * @return The number of exits
     */
    public int getNumExits() {
        return exits.size();
    }

    /**
     * Returns a random exit connected to the room
     * @return A random exit connected to the room
     */
    public Exit randomExit() {
        if (exits.size() == 0) {
            Logger.getLogger("FireEscapeLog").log(Level.SEVERE, "There are no exits: " + this);
        }
        return Map.getExit(exits.get((int) (Math.random() * exits.size())));
    }

    /**
     * Checks if the room has a door to outside
     * @return True if the room has a door to outside, false if not
     */
    /*public int hasDoorToOutside() {
    for (int i = 0; i < exits.size(); i++) {
    if (Map.getExit(exits.get(i)).connectedToID(roomID) == Map.outside.getRoomID()) {
    return i;
    }
    }
    return -1;
    }*/
    /**
     * Checks if the room is equal to this
     * @param room The room to be checked
     * @return True if the room ID of the object is equal to the room ID of this
     */
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Room)) {
            return false;
        }
        Room room = (Room) o;
        return this.getRoomID() == room.getRoomID();
    }

    public String toString() {
        return "Room; ID: " + roomID + " " + room + " Injectable: " + injectable;
    }

    public boolean contains(double x, double y) {
        return room.contains(x, y);
    }

    /**
     * Returns if the bot is inside the room or not
     * @param bot The bot to be checked
     * @return True if the bot is inside the room, false if not
     */
    public boolean botInRoom(Bot bot) {
        return botInRoom(bot.getX(), bot.getY(), bot.getCurrentWaypoint());
    }

    public boolean botInRoom(double x, double y, Exit nextExit) {
        boolean bool1 = room.containsBot(x, y); //sigh
        boolean bool2 = (nextExit != null && nextExit.getRect().containsBot(x, y));
        return room.containsBot(x, y) || (nextExit != null && nextExit.getRect().containsBot(x, y));
    }

    public double minDistanceToWall(Bot b) {
        return minDistanceToWall(b.getX(), b.getY());
    }

    public double minDistanceToWall(double x, double y) {
        if (room == null) {
            return Double.POSITIVE_INFINITY; //sigh
        }
        return room.minDistanceToWall(x, y);
    }

    public double area() {
        return room.area();
    }

    public double getX() {
        return room.getX();
    }

    public double getY() {
        return room.getY();
    }

    public double getWidth() {
        return room.getWidth();
    }

    public double getHeight() {
        return room.getHeight();
    }

    public double getCenterX() {
        return room.getCenterX();
    }

    public double getCenterY() {
        return room.getCenterY();
    }

    public boolean totalNoOverlapCheck(double x, double y) {
        for (int i = 0; i < botIDs.size(); i++) {
            Bot b = this.getBot(i);
            if (FireEscapeGUI.distance(x, y, b.getX(), b.getY()) < 2 * BotAI.botRadius) {
                return false;
            }
        }
        return true;
    }
}
